convert garmin_fit to Format class. (#544)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Tue, 21 Apr 2020 20:38:47 +0000 (14:38 -0600)
committerGitHub <noreply@github.com>
Tue, 21 Apr 2020 20:38:47 +0000 (14:38 -0600)
Additionally, modernize fit reader:
use a container to hold field information for a message.
pass fit fields and message defs by const reference.

CMakeLists.txt
GPSBabel.pro
Makefile.in
garmin_fit.cc
garmin_fit.h [new file with mode: 0644]
vecs.h

index d7c64e7e77376564872301c40c3d2d59d885a956..eb17c65611338adce2adac40b12c01c55421c487 100644 (file)
@@ -114,6 +114,7 @@ set(HEADERS
   format.h
   formspec.h
   garmin_device_xml.h
+  garmin_fit.h
   garmin_fs.h
   garmin_gpi.h
   garmin_icon_tables.h
index 691b784ec5e39df488e346fbbada59f490416c35..a2316cd4b7eb20c032afd55d17295236049fb88a 100644 (file)
@@ -99,6 +99,7 @@ HEADERS =  \
        format.h \
        formspec.h \
        garmin_device_xml.h \
+       garmin_fit.h \
        garmin_fs.h \
        garmin_gpi.h \
        garmin_icon_tables.h \
index 03de36ec2ae5f5b7fa75b6c40473d2d4460e89ad..4d39de46f832150eea8316df79974038b23a85d2 100644 (file)
@@ -508,7 +508,7 @@ filter_vecs.o: filter_vecs.cc defs.h config.h zlib/zlib.h zlib/zconf.h \
   duplicate.h height.h heightgrid.h interpolate.h nukedata.h polygon.h \
   position.h radius.h reverse_route.h smplrout.h sort.h stackfilter.h \
   swapdata.h trackfilter.h transform.h validate.h gbversion.h vecs.h \
-  format.h energympro.h geojson.h src/core/file.h ggv_bin.h \
+  format.h energympro.h garmin_fit.h geojson.h src/core/file.h ggv_bin.h \
   globalsat_sport.h gpx.h src/core/xmlstreamwriter.h src/core/xmltag.h \
   legacyformat.h lowranceusr.h mynav.h qstarz_bl_1000.h nmea.h random.h \
   shape.h shapelib/shapefil.h subrip.h xcsv.h garmin_fs.h jeeps/gps.h \
@@ -533,18 +533,19 @@ garmin.o: garmin.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \
   jeeps/gpsread.h jeeps/gpsutil.h jeeps/gpsapp.h jeeps/gpsprot.h \
   jeeps/gpscom.h jeeps/gpsfmt.h jeeps/gpsmath.h jeeps/gpsmem.h \
   jeeps/gpsrqst.h garmin_tables.h grtcirc.h jeeps/gpsserial.h vecs.h \
-  energympro.h geojson.h src/core/file.h ggv_bin.h globalsat_sport.h \
-  gpx.h src/core/xmlstreamwriter.h src/core/xmltag.h legacyformat.h \
-  lowranceusr.h mynav.h qstarz_bl_1000.h nmea.h random.h shape.h \
-  shapelib/shapefil.h subrip.h xcsv.h src/core/textstream.h yahoo.h \
-  xmlgeneric.h
+  energympro.h garmin_fit.h geojson.h src/core/file.h ggv_bin.h \
+  globalsat_sport.h gpx.h src/core/xmlstreamwriter.h src/core/xmltag.h \
+  legacyformat.h lowranceusr.h mynav.h qstarz_bl_1000.h nmea.h random.h \
+  shape.h shapelib/shapefil.h subrip.h xcsv.h src/core/textstream.h \
+  yahoo.h xmlgeneric.h
 garmin_device_xml.o: garmin_device_xml.cc defs.h config.h zlib/zlib.h \
   zlib/zconf.h formspec.h inifile.h gbfile.h session.h \
   src/core/datetime.h src/core/optional.h garmin_device_xml.h \
   xmlgeneric.h
 garmin_fit.o: garmin_fit.cc defs.h config.h zlib/zlib.h zlib/zconf.h \
   formspec.h inifile.h gbfile.h session.h src/core/datetime.h \
-  src/core/optional.h jeeps/gpsmath.h jeeps/gpsport.h
+  src/core/optional.h garmin_fit.h format.h jeeps/gpsmath.h \
+  jeeps/gpsport.h
 garmin_fs.o: garmin_fs.cc defs.h config.h zlib/zlib.h zlib/zconf.h \
   formspec.h inifile.h gbfile.h session.h src/core/datetime.h \
   src/core/optional.h garmin_fs.h jeeps/gps.h jeeps/../defs.h \
@@ -819,10 +820,10 @@ maggeo.o: maggeo.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \
 magproto.o: magproto.cc defs.h config.h zlib/zlib.h zlib/zconf.h \
   formspec.h inifile.h gbfile.h session.h src/core/datetime.h \
   src/core/optional.h explorist_ini.h format.h gbser.h magellan.h vecs.h \
-  energympro.h geojson.h src/core/file.h ggv_bin.h globalsat_sport.h \
-  gpx.h src/core/xmlstreamwriter.h src/core/xmltag.h legacyformat.h \
-  lowranceusr.h mynav.h qstarz_bl_1000.h nmea.h random.h shape.h \
-  shapelib/shapefil.h subrip.h xcsv.h garmin_fs.h jeeps/gps.h \
+  energympro.h garmin_fit.h geojson.h src/core/file.h ggv_bin.h \
+  globalsat_sport.h gpx.h src/core/xmlstreamwriter.h src/core/xmltag.h \
+  legacyformat.h lowranceusr.h mynav.h qstarz_bl_1000.h nmea.h random.h \
+  shape.h shapelib/shapefil.h subrip.h xcsv.h garmin_fs.h jeeps/gps.h \
   jeeps/../defs.h jeeps/gpsport.h jeeps/gpsdevice.h jeeps/gpssend.h \
   jeeps/gpsread.h jeeps/gpsutil.h jeeps/gpsapp.h jeeps/gpsprot.h \
   jeeps/gpscom.h jeeps/gpsfmt.h jeeps/gpsmath.h jeeps/gpsmem.h \
@@ -833,15 +834,15 @@ main.o: main.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \
   discard.h duplicate.h height.h heightgrid.h interpolate.h nukedata.h \
   polygon.h position.h radius.h reverse_route.h smplrout.h sort.h \
   stackfilter.h swapdata.h trackfilter.h transform.h validate.h format.h \
-  src/core/file.h src/core/usasciicodec.h vecs.h energympro.h geojson.h \
-  ggv_bin.h globalsat_sport.h gpx.h src/core/xmlstreamwriter.h \
-  src/core/xmltag.h legacyformat.h lowranceusr.h mynav.h \
-  qstarz_bl_1000.h nmea.h random.h shape.h shapelib/shapefil.h subrip.h \
-  xcsv.h garmin_fs.h jeeps/gps.h jeeps/../defs.h jeeps/gpsport.h \
-  jeeps/gpsdevice.h jeeps/gpssend.h jeeps/gpsread.h jeeps/gpsutil.h \
-  jeeps/gpsapp.h jeeps/gpsprot.h jeeps/gpscom.h jeeps/gpsfmt.h \
-  jeeps/gpsmath.h jeeps/gpsmem.h jeeps/gpsrqst.h src/core/textstream.h \
-  yahoo.h xmlgeneric.h
+  src/core/file.h src/core/usasciicodec.h vecs.h energympro.h \
+  garmin_fit.h geojson.h ggv_bin.h globalsat_sport.h gpx.h \
+  src/core/xmlstreamwriter.h src/core/xmltag.h legacyformat.h \
+  lowranceusr.h mynav.h qstarz_bl_1000.h nmea.h random.h shape.h \
+  shapelib/shapefil.h subrip.h xcsv.h garmin_fs.h jeeps/gps.h \
+  jeeps/../defs.h jeeps/gpsport.h jeeps/gpsdevice.h jeeps/gpssend.h \
+  jeeps/gpsread.h jeeps/gpsutil.h jeeps/gpsapp.h jeeps/gpsprot.h \
+  jeeps/gpscom.h jeeps/gpsfmt.h jeeps/gpsmath.h jeeps/gpsmem.h \
+  jeeps/gpsrqst.h src/core/textstream.h yahoo.h xmlgeneric.h
 mapasia.o: mapasia.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \
   inifile.h gbfile.h session.h src/core/datetime.h src/core/optional.h
 mapbar_track.o: mapbar_track.cc defs.h config.h zlib/zlib.h zlib/zconf.h \
@@ -1056,15 +1057,15 @@ vcf.o: vcf.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \
   jeeps/gpsmath.h jeeps/gpsport.h
 vecs.o: vecs.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \
   inifile.h gbfile.h session.h src/core/datetime.h src/core/optional.h \
-  vecs.h format.h energympro.h geojson.h src/core/file.h ggv_bin.h \
-  globalsat_sport.h gpx.h src/core/xmlstreamwriter.h src/core/xmltag.h \
-  legacyformat.h lowranceusr.h mynav.h qstarz_bl_1000.h nmea.h random.h \
-  shape.h shapelib/shapefil.h subrip.h xcsv.h garmin_fs.h jeeps/gps.h \
-  jeeps/../defs.h jeeps/gpsport.h jeeps/gpsdevice.h jeeps/gpssend.h \
-  jeeps/gpsread.h jeeps/gpsutil.h jeeps/gpsapp.h jeeps/gpsprot.h \
-  jeeps/gpscom.h jeeps/gpsfmt.h jeeps/gpsmath.h jeeps/gpsmem.h \
-  jeeps/gpsrqst.h src/core/textstream.h yahoo.h xmlgeneric.h gbversion.h \
-  src/core/logging.h
+  vecs.h format.h energympro.h garmin_fit.h geojson.h src/core/file.h \
+  ggv_bin.h globalsat_sport.h gpx.h src/core/xmlstreamwriter.h \
+  src/core/xmltag.h legacyformat.h lowranceusr.h mynav.h \
+  qstarz_bl_1000.h nmea.h random.h shape.h shapelib/shapefil.h subrip.h \
+  xcsv.h garmin_fs.h jeeps/gps.h jeeps/../defs.h jeeps/gpsport.h \
+  jeeps/gpsdevice.h jeeps/gpssend.h jeeps/gpsread.h jeeps/gpsutil.h \
+  jeeps/gpsapp.h jeeps/gpsprot.h jeeps/gpscom.h jeeps/gpsfmt.h \
+  jeeps/gpsmath.h jeeps/gpsmem.h jeeps/gpsrqst.h src/core/textstream.h \
+  yahoo.h xmlgeneric.h gbversion.h src/core/logging.h
 vidaone.o: vidaone.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \
   inifile.h gbfile.h session.h src/core/datetime.h src/core/optional.h
 vitosmt.o: vitosmt.cc defs.h config.h zlib/zlib.h zlib/zconf.h formspec.h \
index fdc4b61a330a536d430de707af697092a10c9782..a50c24c5fed869eef0fa16454d461360c25e7e0c 100644 (file)
 
  */
 
-#include <cstdint>
-#include <cstdio>               // for EOF, snprintf
-#include <vector>
-#include <deque>
-#include <utility>
-
-#include <QtCore/QDateTime>     // for QDateTime
-#include <QtCore/QString>       // for QString
+#include <cstdint>            // for uint8_t, uint16_t, uint32_t, int32_t, int8_t, uint64_t
+#include <cstdio>             // for EOF, SEEK_SET, snprintf
+#include <deque>              // for deque, _Deque_iterator, operator!=
+#include <memory>             // for allocator_traits<>::value_type
+#include <utility>            // for pair
+#include <vector>             // for vector
+
+#include <QtCore/QByteArray>  // for QByteArray
+#include <QtCore/QDateTime>   // for QDateTime
+#include <QtCore/QString>     // for QString
+#include <QtCore/Qt>          // for CaseInsensitive
 
 #include "defs.h"
-#include "gbfile.h"             // for gbfgetc, gbfread, gbfclose, gbfgetuint16, gbfgetuint32, gbfile, gbfopen_le
-#include "jeeps/gpsmath.h"      // for GPS_Math_Semi_To_Deg
+#include "garmin_fit.h"
+#include "gbfile.h"           // for gbfputc, gbfputuint16, gbfputuint32, gbfgetc, gbfread, gbfseek, gbfclose, gbfgetuint16, gbfopen_le, gbfputint32, gbfflush, gbfgetuint32, gbfputs, gbftell, gbfwrite, gbfile, gbsize_t
+#include "jeeps/gpsmath.h"    // for GPS_Math_Semi_To_Deg, GPS_Math_Gtime_To_Utime, GPS_Math_Deg_To_Semi, GPS_Math_Utime_To_Gtime
 
 
 #define MYNAME "fit"
 
-// constants for global IDs
-const int kIdFileId = 0;
-const int kIdDeviceSettings = 0;
-const int kIdLap = 19;
-const int kIdRecord = 20;
-const int kIdEvent = 21;
-const int kIdCourse = 31;
-const int kIdCoursePoint = 32;
-
-// constants for local IDs (for writing)
-const int kWriteLocalIdFileId = 0;
-const int kWriteLocalIdCourse = 1;
-const int kWriteLocalIdLap = 2;
-const int kWriteLocalIdEvent = 3;
-const int kWriteLocalIdCoursePoint = 4;
-const int kWriteLocalIdRecord = 5;
-
-// constants for message fields
-// for all global IDs
-const int kFieldTimestamp = 253;
-const int kFieldMessageIndex = 254;
-// for global ID: file id
-const int kFieldType = 0;
-const int kFieldManufacturer = 1;
-const int kFieldProduct = 2;
-const int kFieldTimeCreated = 4;
-// for global ID: device settings
-const int kFieldGlobalUtcOffset = 4;
-// for global ID: lap
-const int kFieldStartTime = 2;
-const int kFieldStartLatitude = 3;
-const int kFieldStartLongitude = 4;
-const int kFieldEndLatitude = 5;
-const int kFieldEndLongitude = 6;
-const int kFieldElapsedTime = 7;
-const int kFieldTotalTimerTime = 8;
-const int kFieldTotalDistance = 9;
-const int kFieldAvgSpeed = 13;
-const int kFieldMaxSpeed = 14;
-// for global ID: record
-const int kFieldLatitude = 0;
-const int kFieldLongitude = 1;
-const int kFieldAltitude = 2;
-const int kFieldHeartRate = 3;
-const int kFieldCadence = 4;
-const int kFieldDistance = 5;
-const int kFieldSpeed = 6;
-const int kFieldPower = 7;
-const int kFieldTemperature = 13;
-const int kFieldEnhancedSpeed = 73;
-const int kFieldEnhancedAltitude = 78;
-// for global ID: event
-const int kFieldEvent = 0;
-const int kEnumEventTimer = 0;
-const int kFieldEventType = 1;
-const int kEnumEventTypeStart = 0;
-const int kFieldEventGroup = 4;
-// for global ID: course
-const int kFieldSport = 4;
-const int kFieldName = 5;
-// for global ID: course point
-const int kFieldCPTimeStamp = 1;
-const int kFieldCPPositionLat = 2;
-const int kFieldCPPositionLong = 3;
-const int kFieldCPDistance = 4;
-const int kFieldCPName = 6;
-const int kFieldCPType = 5;
-
-// For developer fields as a non conflicting id
-const int kFieldInvalid = 255;
-
-// types for message definitions
-const int kTypeEnum = 0x00;
-const int kTypeUint8 = 0x02;
-const int kTypeString = 0x07;
-const int kTypeUint16 = 0x84;
-const int kTypeSint32 = 0x85;
-const int kTypeUint32 = 0x86;
-
-// misc. constants for message fields
-const int kFileCourse = 0x06;
-const int kEventTimer = 0x00;
-const int kEventTypeStart = 0x00;
-const int kEventTypeStopDisableAll = 0x09;
-const int kCoursePointTypeGeneric = 0x00;
-const int kCoursePointTypeLeft = 0x06;
-const int kCoursePointTypeRight = 0x07;
-
-const int kWriteHeaderLen = 12;
-const int kWriteHeaderCrcLen = 14;
-
-const double kSynthSpeed = 10.0 * 1000 / 3600; /* speed in m/s */
-
-static char* opt_allpoints = nullptr;
-static int lap_ct = 0;
-static bool new_trkseg = false;
-static bool write_header_msgs = false;
-
-
-static
-QVector<arglist_t> fit_args = {
-  {
-    "allpoints", &opt_allpoints,
-    "Read all points even if latitude or longitude is missing",
-    nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
-  },
-};
-
-const std::vector<std::pair<QString, int> > kCoursePointTypeMapping = {
-  {"left", kCoursePointTypeLeft},
-  {"links", kCoursePointTypeLeft},
-  {"gauche", kCoursePointTypeLeft},
-  {"izquierda", kCoursePointTypeLeft},
-  {"sinistra", kCoursePointTypeLeft},
-
-  {"right", kCoursePointTypeRight},
-  {"rechts", kCoursePointTypeRight},
-  {"droit", kCoursePointTypeRight},
-  {"derecha", kCoursePointTypeRight},
-  {"destro", kCoursePointTypeRight},
-};
-
-
-struct fit_field_t {
-  int id;
-  int size;
-  int type;
-};
-
-struct fit_message_def {
-  int endian;
-  int global_id;
-  int num_fields;
-  fit_field_t* fields;
-};
-
-static struct {
-  int len;
-  int endian;
-  route_head* track;
-  uint32_t last_timestamp;
-  uint32_t global_utc_offset;
-  fit_message_def message_def[16];
-} fit_data;
-
-struct FitCourseRecordPoint {
-  FitCourseRecordPoint(const Waypoint &wpt, bool is_course_point, unsigned int course_point_type = kCoursePointTypeGeneric)
-      : lat(wpt.latitude),
-        lon(wpt.longitude),
-        altitude(wpt.altitude),
-        speed(WAYPT_HAS((&wpt), speed) ? wpt.speed : -1),
-        odometer_distance(wpt.odometer_distance),
-        creation_time(wpt.creation_time),
-        shortname(wpt.shortname),
-        is_course_point(is_course_point),
-        course_point_type(course_point_type) { }
-  double lat, lon, altitude;
-  double speed, odometer_distance;
-  gpsbabel::DateTime creation_time;
-  QString shortname;
-  bool is_course_point;
-  unsigned int course_point_type;
-};
-
-std::deque<FitCourseRecordPoint> course, waypoints;
-
-
-static gbfile* fin;
-static gbfile* fout;
+// Until c++17 we have to define odr-used constexpr static data members at namespace scope.
+#if __cplusplus < 201703L
+constexpr int GarminFitFormat::kTypeEnum;
+constexpr int GarminFitFormat::kTypeUint8;
+constexpr int GarminFitFormat::kTypeString;
+constexpr int GarminFitFormat::kTypeUint16;
+constexpr int GarminFitFormat::kTypeSint32;
+constexpr int GarminFitFormat::kTypeUint32;
+constexpr int GarminFitFormat::kCoursePointTypeLeft;
+constexpr int GarminFitFormat::kCoursePointTypeRight;
+#endif
 
 /*******************************************************************************
 * %%%        global callbacks called by gpsbabel main process              %%% *
 *******************************************************************************/
 
-static void
-fit_rd_init(const QString& fname)
+void
+GarminFitFormat::rd_init(const QString& fname)
 {
   fin = gbfopen_le(fname, "rb", MYNAME);
 }
 
-static void
-fit_rd_deinit()
+void
+GarminFitFormat::rd_deinit()
 {
-  for (auto &local_id : fit_data.message_def) {
-    fit_message_def* def = &local_id;
-    if (def->fields) {
-      xfree(def->fields);
-      def->fields = nullptr;
-    }
+  for (auto& local_id : fit_data.message_def) {
+    // QList::clear() will not deallocate
+    local_id.fields = QList<fit_field_t>();
   }
 
   gbfclose(fin);
 }
 
-static void
-fit_wr_init(const QString& fname)
+void
+GarminFitFormat::wr_init(const QString& fname)
 {
   fout = gbfopen_le(fname, "w+b", MYNAME);
 }
 
-static void
-fit_wr_deinit()
+void
+GarminFitFormat::wr_deinit()
 {
   gbfclose(fout);
 }
 
-
 /*******************************************************************************
 * fit_parse_header- parse the global FIT header
 *******************************************************************************/
-static void
-fit_parse_header()
+void
+GarminFitFormat::fit_parse_header()
 {
   char sig[4];
 
@@ -293,8 +135,8 @@ fit_parse_header()
   fit_data.global_utc_offset = 0;
 }
 
-static uint8_t
-fit_getuint8()
+uint8_t
+GarminFitFormat::fit_getuint8()
 {
   if (fit_data.len == 0) {
     // fail gracefully for GARMIN Edge 800 with newest firmware, seems to write a wrong record length
@@ -311,11 +153,10 @@ fit_getuint8()
   }
   fit_data.len--;
   return (uint8_t)val;
-
 }
 
-static uint16_t
-fit_getuint16()
+uint16_t
+GarminFitFormat::fit_getuint16()
 {
   char buf[2];
 
@@ -330,11 +171,10 @@ fit_getuint16()
   } else {
     return le_read16(buf);
   }
-
 }
 
-static uint32_t
-fit_getuint32()
+uint32_t
+GarminFitFormat::fit_getuint32()
 {
   char buf[4];
 
@@ -349,22 +189,19 @@ fit_getuint32()
   } else {
     return le_read32(buf);
   }
-
 }
 
-static void
-fit_parse_definition_message(uint8_t header)
+void
+GarminFitFormat::fit_parse_definition_message(uint8_t header)
 {
   int local_id = header & 0x0f;
   fit_message_def* def = &fit_data.message_def[local_id];
 
-  if (def->fields) {
-    xfree(def->fields);
-  }
+  def->fields = QList<fit_field_t>();
 
   // first byte is reserved.  It's usually 0 and we don't know what it is,
   // but we've seen some files that are 0x40.  So we just read it and toss it.
-  int i = fit_getuint8();
+  (void) fit_getuint8();
 
   // second byte is endianness
   def->endian = fit_getuint8();
@@ -377,49 +214,44 @@ fit_parse_definition_message(uint8_t header)
   def->global_id = fit_getuint16();
 
   // byte 5 has the number of records in the remainder of the definition message
-  def->num_fields = fit_getuint8();
+  int num_fields = fit_getuint8();
   if (global_opts.debug_level >= 8) {
-    debug_print(8,"%s: definition message contains %d records\n",MYNAME, def->num_fields);
-  }
-  if (def->num_fields == 0) {
-    def->fields = (fit_field_t*) xmalloc(sizeof(fit_field_t));
+    debug_print(8,"%s: definition message contains %d records\n",MYNAME, num_fields);
   }
 
   // remainder of the definition message is data at one byte per field * 3 fields
-  if (def->num_fields > 0) {
-    def->fields = (fit_field_t*) xmalloc(def->num_fields * sizeof(fit_field_t));
-    for (i = 0; i < def->num_fields; i++) {
-      def->fields[i].id   = fit_getuint8();
-      def->fields[i].size = fit_getuint8();
-      def->fields[i].type = fit_getuint8();
-      if (global_opts.debug_level >= 8) {
-        debug_print(8,"%s: record %d  ID: %d  SIZE: %d  TYPE: %d  fit_data.len=%d\n",
-                    MYNAME, i, def->fields[i].id, def->fields[i].size, def->fields[i].type,fit_data.len);
-      }
-
+  for (int i = 0; i < num_fields; ++i) {
+    int id   = fit_getuint8();
+    int size = fit_getuint8();
+    int type = fit_getuint8();
+    fit_field_t field = {id, size, type};
+    if (global_opts.debug_level >= 8) {
+      debug_print(8,"%s: record %d  ID: %d  SIZE: %d  TYPE: %d  fit_data.len=%d\n",
+                  MYNAME, i, field.id, field.size, field.type, fit_data.len);
     }
+    def->fields.append(field);
   }
 
   // If we have developer fields (since version 2.0) they must be read too
   // These is one byte containing the number of fields and 3 bytes for every field.
   // So this is identical to the normal fields but the meaning of the content is different.
   //
-  // Currently we just want to ignore the developer fields because they are not meant 
+  // Currently we just want to ignore the developer fields because they are not meant
   // to hold relevant data we need (currently handle) for the conversion.
 
   // For simplicity using the existing infrastructure we do it in the following way:
-  //   * We read it in as normal fields 
-  //   * We set the field id to kFieldInvalid so that it do not interfere with valid id's from 
-  //     the normal fields.    
-  //       -In our opinion in practice this will not happen, because we do not expect 
+  //   * We read it in as normal fields
+  //   * We set the field id to kFieldInvalid so that it do not interfere with valid id's from
+  //     the normal fields.
+  //       -In our opinion in practice this will not happen, because we do not expect
   //        developer fields e.g. inside lap or record records. But we want to be safe here.
   //   * We do not have to change the type as we did for the id above, because fit_read_field()
-  //     already uses the size information to read the data, if the type does not match the size. 
-  //   
+  //     already uses the size information to read the data, if the type does not match the size.
+  //
   // If we want to change this or if we want to avoid the xrealloc call, we can change
   // it in the future by e.g. extending the fit_message_def struct.
 
-  // Bit 5 of the header specify if we have developer fields in the data message 
+  // Bit 5 of the header specify if we have developer fields in the data message
   bool hasDevFields = static_cast<bool>(header & 0x20);
 
   if (hasDevFields) {
@@ -431,27 +263,26 @@ fit_parse_definition_message(uint8_t header)
       return;
     }
 
-    int numOfFields = def->num_fields+numOfDevFields;
-    def->fields = (fit_field_t*) xrealloc(def->fields, numOfFields * sizeof(fit_field_t));
-    for (i = def->num_fields; i < numOfFields; i++) {
-      def->fields[i].id   = fit_getuint8();
-      def->fields[i].size = fit_getuint8();
-      def->fields[i].type = fit_getuint8();
+    int numOfFields = num_fields + numOfDevFields;
+    for (int i = num_fields; i < numOfFields; ++i) {
+      int id   = fit_getuint8();
+      int size = fit_getuint8();
+      int type = fit_getuint8();
+      fit_field_t field = {id, size, type};
       if (global_opts.debug_level >= 8) {
         debug_print(8,"%s: developer record %d  ID: %d  SIZE: %d  TYPE: %d  fit_data.len=%d\n",
-                    MYNAME, i-def->num_fields, def->fields[i].id, def->fields[i].size, def->fields[i].type,fit_data.len);
+                    MYNAME, i - num_fields, field.id, field.size, field.type, fit_data.len);
       }
-      // Because we parse developer fields like normal fields and we do not want 
+      // Because we parse developer fields like normal fields and we do not want
       // that the field id interfere which valid id's from the normal fields
-      def->fields[i].id = kFieldInvalid;
-
+      field.id = kFieldInvalid;
+      def->fields.append(field);
     }
-    def->num_fields = numOfFields;
   }
 }
 
-static uint32_t
-fit_read_field(fit_field_t* f)
+uint32_t
+GarminFitFormat::fit_read_field(const fit_field_t& f)
 {
   /* https://forums.garmin.com/showthread.php?223645-Vivoactive-problems-plus-suggestions-for-future-firmwares&p=610929#post610929
    * Per section 4.2.1.4.2 of the FIT Protocol the size of a field may be a
@@ -462,20 +293,19 @@ fit_read_field(fit_field_t* f)
    */
   // In the case that the field contains one value of the indicated type we return that value,
   // otherwise we just skip over the data.
-  int i;
 
   if (global_opts.debug_level >= 8) {
-    debug_print(8,"%s: fit_read_field: read data field with f->type=0x%X and f->size=%d fit_data.len=%d\n",
-                MYNAME, f->type, f->size, fit_data.len);
+    debug_print(8,"%s: fit_read_field: read data field with f.type=0x%X and f.size=%d fit_data.len=%d\n",
+                MYNAME, f.type, f.size, fit_data.len);
   }
-  switch (f->type) {
+  switch (f.type) {
   case 0: // enum
   case 1: // sint8
   case 2: // uint8
-    if (f->size == 1) {
+    if (f.size == 1) {
       return fit_getuint8();
     } else { // ignore array data
-      for (i = 0; i < f->size; i++) {
+      for (int i = 0; i < f.size; ++i) {
         fit_getuint8();
       }
       if (global_opts.debug_level >= 8) {
@@ -485,10 +315,10 @@ fit_read_field(fit_field_t* f)
     }
   case 0x83: // sint16
   case 0x84: // uint16
-    if (f->size == 2) {
+    if (f.size == 2) {
       return fit_getuint16();
     } else { // ignore array data
-      for (i = 0; i < f->size; i++) {
+      for (int i = 0; i < f.size; ++i) {
         fit_getuint8();
       }
       if (global_opts.debug_level >= 8) {
@@ -498,10 +328,10 @@ fit_read_field(fit_field_t* f)
     }
   case 0x85: // sint32
   case 0x86: // uint32
-    if (f->size == 4) {
+    if (f.size == 4) {
       return fit_getuint32();
     } else { // ignore array data
-      for (i = 0; i < f->size; i++) {
+      for (int i = 0; i < f.size; ++i) {
         fit_getuint8();
       }
       if (global_opts.debug_level >= 8) {
@@ -510,7 +340,7 @@ fit_read_field(fit_field_t* f)
       return -1;
     }
   default: // Ignore everything else for now.
-    for (i = 0; i < f->size; i++) {
+    for (int i = 0; i < f.size; ++i) {
       fit_getuint8();
     }
     if (global_opts.debug_level >= 8) {
@@ -520,8 +350,8 @@ fit_read_field(fit_field_t* f)
   }
 }
 
-static void
-fit_parse_data(fit_message_def* def, int time_offset)
+void
+GarminFitFormat::fit_parse_data(const fit_message_def& def, int time_offset)
 {
   uint32_t timestamp = fit_data.last_timestamp + time_offset;
   int32_t lat = 0x7fffffff;
@@ -532,7 +362,6 @@ fit_parse_data(fit_message_def* def, int time_offset)
   uint8_t cadence = 0xff;
   uint16_t power = 0xffff;
   int8_t temperature = 0x7f;
-  Waypoint* waypt;
   int32_t startlat = 0x7fffffff;
   int32_t startlon = 0x7fffffff;
   int32_t endlat = 0x7fffffff;
@@ -540,32 +369,31 @@ fit_parse_data(fit_message_def* def, int time_offset)
   uint32_t starttime = 0; // ??? default ?
   uint8_t event = 0xff;
   uint8_t eventtype = 0xff;
-  char cbuf[10];
-  Waypoint* lappt;  // WptPt in gpx
 
   if (global_opts.debug_level >= 7) {
-    debug_print(7,"%s: parsing fit data ID %d with num_fields=%d\n", MYNAME, def->global_id, def->num_fields);
+    debug_print(7,"%s: parsing fit data ID %d with num_fields=%d\n", MYNAME, def.global_id, def.fields.size());
   }
-  for (int i = 0; i < def->num_fields; i++) {
+  for (int i = 0; i < def.fields.size(); ++i) {
     if (global_opts.debug_level >= 7) {
       debug_print(7,"%s: parsing field %d\n", MYNAME, i);
     }
-    fit_field_t* f = &def->fields[i];
+    const fit_field_t& f = def.fields.at(i);
     uint32_t val = fit_read_field(f);
-    if (f->id == kFieldTimestamp) {
+    if (f.id == kFieldTimestamp) {
       if (global_opts.debug_level >= 7) {
         debug_print(7,"%s: parsing fit data: timestamp=%d\n", MYNAME, val);
       }
       timestamp = val;
       // if the timestamp is < 0x10000000, this value represents
       // system time; to convert it to UTC, add the global utc offset to it
-      if (timestamp < 0x10000000)
+      if (timestamp < 0x10000000) {
         timestamp += fit_data.global_utc_offset;
+      }
       fit_data.last_timestamp = timestamp;
     } else {
-      switch (def->global_id) {
+      switch (def.global_id) {
       case kIdDeviceSettings: // device settings message
-        switch (f->id) {
+        switch (f.id) {
         case kFieldGlobalUtcOffset:
           if (global_opts.debug_level >= 7) {
             debug_print(7,"%s: parsing fit data: global utc_offset=%d\n", MYNAME, val);
@@ -574,15 +402,15 @@ fit_parse_data(fit_message_def* def, int time_offset)
           break;
         default:
           if (global_opts.debug_level >= 1) {
-            debug_print(1, "%s: unrecognized data type in GARMIN FIT device settings: f->id=%d\n", MYNAME, f->id);
+            debug_print(1, "%s: unrecognized data type in GARMIN FIT device settings: f.id=%d\n", MYNAME, f.id);
           }
           break;
-        } // switch (f->id)
-        // end of case def->global_id = kIdDeviceSettings
+        } // switch (f.id)
+        // end of case def.global_id = kIdDeviceSettings
         break;
 
       case kIdRecord: // record message - trkType is a track
-        switch (f->id) {
+        switch (f.id) {
         case kFieldLatitude:
           if (global_opts.debug_level >= 7) {
             debug_print(7,"%s: parsing fit data: lat=%d\n", MYNAME, val);
@@ -600,7 +428,7 @@ fit_parse_data(fit_message_def* def, int time_offset)
             debug_print(7,"%s: parsing fit data: alt=%d\n", MYNAME, val);
           }
           if (val != 0xffff) {
-              alt = val;
+            alt = val;
           }
           break;
         case kFieldHeartRate:
@@ -618,7 +446,7 @@ fit_parse_data(fit_message_def* def, int time_offset)
         case kFieldDistance:
           // NOTE: 5 is DISTANCE in cm ... unused.
           if (global_opts.debug_level >= 7) {
-            debug_print(7, "%s: unrecognized data type in GARMIN FIT record: f->id=%d\n", MYNAME, f->id);
+            debug_print(7, "%s: unrecognized data type in GARMIN FIT record: f.id=%d\n", MYNAME, f.id);
           }
           break;
         case kFieldSpeed:
@@ -626,7 +454,7 @@ fit_parse_data(fit_message_def* def, int time_offset)
             debug_print(7,"%s: parsing fit data: speed=%d\n", MYNAME, val);
           }
           if (val != 0xffff) {
-              speed = val;
+            speed = val;
           }
           break;
         case kFieldPower:
@@ -646,7 +474,7 @@ fit_parse_data(fit_message_def* def, int time_offset)
             debug_print(7,"%s: parsing fit data: enhanced_speed=%d\n", MYNAME, val);
           }
           if (val != 0xffff) {
-              speed = val;
+            speed = val;
           }
           break;
         case kFieldEnhancedAltitude:
@@ -654,20 +482,20 @@ fit_parse_data(fit_message_def* def, int time_offset)
             debug_print(7,"%s: parsing fit data: enhanced_altitude=%d\n", MYNAME, val);
           }
           if (val != 0xffff) {
-              alt = val;
+            alt = val;
           }
           break;
         default:
           if (global_opts.debug_level >= 1) {
-            debug_print(1, "%s: unrecognized data type in GARMIN FIT record: f->id=%d\n", MYNAME, f->id);
+            debug_print(1, "%s: unrecognized data type in GARMIN FIT record: f.id=%d\n", MYNAME, f.id);
           }
           break;
-        } // switch (f->id)
-        // end of case def->global_id = kIdRecord
+        } // switch (f.id)
+        // end of case def.global_id = kIdRecord
         break;
 
       case kIdLap: // lap wptType , endlat+lon is wpt
-        switch (f->id) {
+        switch (f.id) {
         case kFieldStartTime:
           if (global_opts.debug_level >= 7) {
             debug_print(7,"%s: parsing fit data: starttime=%d\n", MYNAME, val);
@@ -712,15 +540,15 @@ fit_parse_data(fit_message_def* def, int time_offset)
           break;
         default:
           if (global_opts.debug_level >= 1) {
-            debug_print(1, "%s: unrecognized data type in GARMIN FIT lap: f->id=%d\n", MYNAME, f->id);
+            debug_print(1, "%s: unrecognized data type in GARMIN FIT lap: f.id=%d\n", MYNAME, f.id);
           }
           break;
-        } // switch (f->id)
-        // end of case def->global_id = kIdLap
+        } // switch (f.id)
+        // end of case def.global_id = kIdLap
         break;
 
       case kIdEvent:
-        switch (f->id) {
+        switch (f.id) {
         case kFieldEvent:
           if (global_opts.debug_level >= 7) {
             debug_print(7,"%s: parsing fit data: event=%d\n", MYNAME, val);
@@ -733,43 +561,44 @@ fit_parse_data(fit_message_def* def, int time_offset)
           }
           eventtype = val;
           break;
-        } // switch (f->id)
-        // end of case def->global_id = kIdEvent
+        } // switch (f.id)
+        // end of case def.global_id = kIdEvent
         break;
       default:
         if (global_opts.debug_level >= 1) {
-          debug_print(1, "%s: unrecognized/unhandled global ID for GARMIN FIT: %d\n", MYNAME, def->global_id);
+          debug_print(1, "%s: unrecognized/unhandled global ID for GARMIN FIT: %d\n", MYNAME, def.global_id);
         }
         break;
-      } // switch (def->global_id)
+      } // switch (def.global_id)
     }
   }
 
   if (global_opts.debug_level >= 7) {
-    debug_print(7,"%s: storing fit data with num_fields=%d\n", MYNAME, def->num_fields);
+    debug_print(7,"%s: storing fit data with num_fields=%d\n", MYNAME, def.fields.size());
   }
-  switch (def->global_id) {
-  case kIdLap: // lap message
+  switch (def.global_id) {
+  case kIdLap: // lap message
     if (endlat == 0x7fffffff || endlon == 0x7fffffff) {
       break;
     }
     if (global_opts.debug_level >= 7) {
-      debug_print(7,"%s: storing fit data LAP %d\n", MYNAME, def->global_id);
+      debug_print(7,"%s: storing fit data LAP %d\n", MYNAME, def.global_id);
     }
-    lappt = new Waypoint;
+    auto* lappt = new Waypoint;
     lappt->latitude = GPS_Math_Semi_To_Deg(endlat);
     lappt->longitude = GPS_Math_Semi_To_Deg(endlon);
-    lap_ct++;
-    snprintf(cbuf, sizeof(cbuf), "LAP%03d", lap_ct);
+    char cbuf[10];
+    snprintf(cbuf, sizeof(cbuf), "LAP%03d", ++lap_ct);
     lappt->shortname = cbuf;
     waypt_add(lappt);
-    break;
-  case kIdRecord: // record message
+  }
+  break;
+  case kIdRecord: { // record message
     if ((lat == 0x7fffffff || lon == 0x7fffffff) && !opt_allpoints) {
       break;
     }
 
-    waypt = new Waypoint;
+    auto* waypt = new Waypoint;
     if (lat != 0x7fffffff) {
       waypt->latitude = GPS_Math_Semi_To_Deg(lat);
     }
@@ -800,7 +629,8 @@ fit_parse_data(fit_message_def* def, int time_offset)
       new_trkseg = false;
     }
     track_add_wpt(fit_data.track, waypt);
-    break;
+  }
+  break;
   case kIdEvent: // event message
     if (event == kEnumEventTimer && eventtype == kEnumEventTypeStart) {
       // Start event, start new track segment. Note: We don't do this
@@ -813,35 +643,33 @@ fit_parse_data(fit_message_def* def, int time_offset)
   }
 }
 
-static void
-fit_parse_data_message(uint8_t header)
+void
+GarminFitFormat::fit_parse_data_message(uint8_t header)
 {
   int local_id = header & 0x0f;
-  fit_message_def* def = &fit_data.message_def[local_id];
-  fit_parse_data(def, 0);
+  fit_parse_data(fit_data.message_def[local_id], 0);
 }
 
-static void
-fit_parse_compressed_message(uint8_t header)
+void
+GarminFitFormat::fit_parse_compressed_message(uint8_t header)
 {
   int local_id = (header >> 5) & 3;
-  fit_message_def* def = &fit_data.message_def[local_id];
-  fit_parse_data(def, header & 0x1f);
+  fit_parse_data(fit_data.message_def[local_id], header & 0x1f);
 }
 
 /*******************************************************************************
 * fit_parse_record- parse each record in the file
 *******************************************************************************/
-static void
-fit_parse_record()
+void
+GarminFitFormat::fit_parse_record()
 {
   uint8_t header = fit_getuint8();
   // high bit 7 set -> compressed message (0 for normal)
   // second bit 6 set -> 0 for data message, 1 for definition message
-  // bit 5 -> message type specific 
-  //    definition message: Bit set means that we have additional Developer Field definitions 
+  // bit 5 -> message type specific
+  //    definition message: Bit set means that we have additional Developer Field definitions
   //                        behind the field definitions inside the record content
-  //    data message: currently not used 
+  //    data message: currently not used
   // bit 4 -> reserved
   // bits 3..0 -> local message type
   if (header & 0x80) {
@@ -870,8 +698,8 @@ fit_parse_record()
 * - parse the header
 * - parse all the records in the file
 *******************************************************************************/
-static void
-fit_read()
+void
+GarminFitFormat::read()
 {
   fit_parse_header();
 
@@ -889,71 +717,24 @@ fit_read()
 * FIT writing
 *******************************************************************************/
 
-const static std::vector<fit_field_t> fit_msg_fields_file_id = {
-  // field id,            size, type
-  { kFieldType,           0x01, kTypeEnum   },
-  { kFieldManufacturer,   0x02, kTypeUint16 },
-  { kFieldProduct,        0x02, kTypeUint16 },
-  { kFieldTimeCreated,    0x04, kTypeUint32 },
-};
-const static std::vector<fit_field_t> fit_msg_fields_course = {
-  { kFieldName,           0x10, kTypeString },
-  { kFieldSport,          0x01, kTypeEnum   },
-};
-const static std::vector<fit_field_t> fit_msg_fields_lap = {
-  { kFieldTimestamp,      0x04, kTypeUint32 },
-  { kFieldStartTime,      0x04, kTypeUint32 },
-  { kFieldStartLatitude,  0x04, kTypeSint32 },
-  { kFieldStartLongitude, 0x04, kTypeSint32 },
-  { kFieldEndLatitude,    0x04, kTypeSint32 },
-  { kFieldEndLongitude,   0x04, kTypeSint32 },
-  { kFieldElapsedTime,    0x04, kTypeUint32 },
-  { kFieldTotalTimerTime, 0x04, kTypeUint32 },
-  { kFieldTotalDistance,  0x04, kTypeUint32 },
-  { kFieldAvgSpeed,       0x02, kTypeUint16 },
-  { kFieldMaxSpeed,       0x02, kTypeUint16 },
-};
-const static std::vector<fit_field_t> fit_msg_fields_event = {
-  { kFieldTimestamp,      0x04, kTypeUint32 },
-  { kFieldEvent,          0x01, kTypeEnum   },
-  { kFieldEventType,      0x01, kTypeEnum   },
-  { kFieldEventGroup,     0x01, kTypeUint8  },
-};
-const static std::vector<fit_field_t> fit_msg_fields_course_point = {
-  { kFieldCPTimeStamp,    0x04, kTypeUint32 },
-  { kFieldCPPositionLat,  0x04, kTypeSint32 },
-  { kFieldCPPositionLong, 0x04, kTypeSint32 },
-  { kFieldCPDistance,     0x04, kTypeUint32 },
-  { kFieldCPName,         0x10, kTypeString },
-  { kFieldCPType,         0x01, kTypeEnum   },
-};
-const static std::vector<fit_field_t> fit_msg_fields_record = {
-  { kFieldTimestamp,      0x04, kTypeUint32 },
-  { kFieldLatitude,       0x04, kTypeSint32 },
-  { kFieldLongitude,      0x04, kTypeSint32 },
-  { kFieldDistance,       0x04, kTypeUint32 },
-  { kFieldAltitude,       0x02, kTypeUint16 },
-  { kFieldSpeed,          0x02, kTypeUint16 },
-};
-
-
-static void
-fit_write_message_def(uint8_t local_id, uint16_t global_id, const std::vector<fit_field_t> &fields) {
+void
+GarminFitFormat::fit_write_message_def(uint8_t local_id, uint16_t global_id, const std::vector<fit_field_t>& fields) const
+{
   gbfputc(0x40 | local_id, fout); // Local ID
   gbfputc(0, fout); // Reserved
   gbfputc(0, fout); // Little endian
   gbfputuint16(global_id, fout); // Global ID
   gbfputc(fields.size(), fout); // Number of fields
-  for (auto &&field : fields) {
+  for (auto&& field : fields) {
     gbfputc(field.id, fout); // Field definition number
     gbfputc(field.size, fout); // Field size in bytes
     gbfputc(field.type, fout); // Field type
   }
 }
 
-
-static uint16_t
-fit_crc16(uint8_t data, uint16_t crc) {
+uint16_t
+GarminFitFormat::fit_crc16(uint8_t data, uint16_t crc)
+{
   static const uint16_t crc_table[] = {
     0x0000, 0xcc01, 0xd801, 0x1400, 0xf001, 0x3c00, 0x2800, 0xe401,
     0xa001, 0x6c00, 0x7800, 0xb401, 0x5000, 0x9c01, 0x8801, 0x4400
@@ -964,9 +745,9 @@ fit_crc16(uint8_t data, uint16_t crc) {
   return crc;
 }
 
-
-static void
-fit_write_timestamp(const gpsbabel::DateTime &t) {
+void
+GarminFitFormat::fit_write_timestamp(const gpsbabel::DateTime& t) const
+{
   uint32_t t_fit;
   if (t.isValid() && t.toTime_t() >= (unsigned int)GPS_Math_Gtime_To_Utime(0)) {
     t_fit = GPS_Math_Utime_To_Gtime(t.toTime_t());
@@ -976,9 +757,9 @@ fit_write_timestamp(const gpsbabel::DateTime &t) {
   gbfputuint32(t_fit, fout);
 }
 
-
-static void
-fit_write_fixed_string(const QString &s, unsigned int len) {
+void
+GarminFitFormat::fit_write_fixed_string(const QString& s, unsigned int len) const
+{
   QString trimmed(s);
   QByteArray u8buf;
 
@@ -999,9 +780,9 @@ fit_write_fixed_string(const QString &s, unsigned int len) {
   gbfwrite(u8buf.data(), len, 1, fout);
 }
 
-
-static void
-fit_write_position(double pos) {
+void
+GarminFitFormat::fit_write_position(double pos) const
+{
   if (pos >= -180 && pos < 180) {
     gbfputint32(GPS_Math_Deg_To_Semi(pos), fout);
   } else {
@@ -1009,12 +790,12 @@ fit_write_position(double pos) {
   }
 }
 
-
 // Note: The data fields written using fit_write_msg_*() below need to match
 // the message field definitions in fit_msg_fields_* above!
-static void
-fit_write_msg_file_id(uint8_t type, uint16_t manufacturer, uint16_t product,
-                      const gpsbabel::DateTime &time_created) {
+void
+GarminFitFormat::fit_write_msg_file_id(uint8_t type, uint16_t manufacturer, uint16_t product,
+                                       const gpsbabel::DateTime& time_created) const
+{
   gbfputc(kWriteLocalIdFileId, fout);
   gbfputc(type, fout);
   gbfputuint16(manufacturer, fout);
@@ -1022,19 +803,21 @@ fit_write_msg_file_id(uint8_t type, uint16_t manufacturer, uint16_t product,
   fit_write_timestamp(time_created);
 }
 
-static void
-fit_write_msg_course(const QString &name, uint8_t sport) {
+void
+GarminFitFormat::fit_write_msg_course(const QString& name, uint8_t sport) const
+{
   gbfputc(kWriteLocalIdCourse, fout);
   fit_write_fixed_string(name, 0x10);
   gbfputc(sport, fout);
 }
 
-static void
-fit_write_msg_lap(const gpsbabel::DateTime &timestamp, const gpsbabel::DateTime &start_time,
-                  double start_position_lat, double start_position_long,
-                  double end_position_lat, double end_position_long,
-                  uint32_t total_elapsed_time_s, double total_distance_m,
-                  double avg_speed_ms, double max_speed_ms) {
+void
+GarminFitFormat::fit_write_msg_lap(const gpsbabel::DateTime& timestamp, const gpsbabel::DateTime& start_time,
+                                   double start_position_lat, double start_position_long,
+                                   double end_position_lat, double end_position_long,
+                                   uint32_t total_elapsed_time_s, double total_distance_m,
+                                   double avg_speed_ms, double max_speed_ms) const
+{
   gbfputc(kWriteLocalIdLap, fout);
   fit_write_timestamp(timestamp);
   fit_write_timestamp(start_time);
@@ -1066,10 +849,10 @@ fit_write_msg_lap(const gpsbabel::DateTime &timestamp, const gpsbabel::DateTime
   }
 }
 
-
-static void
-fit_write_msg_event(const gpsbabel::DateTime &timestamp,
-                    uint8_t event, uint8_t event_type, uint8_t event_group) {
+void
+GarminFitFormat::fit_write_msg_event(const gpsbabel::DateTime& timestamp,
+                                     uint8_t event, uint8_t event_type, uint8_t event_group) const
+{
   gbfputc(kWriteLocalIdEvent, fout);
   fit_write_timestamp(timestamp);
   gbfputc(event, fout);
@@ -1077,12 +860,12 @@ fit_write_msg_event(const gpsbabel::DateTime &timestamp,
   gbfputc(event_group, fout);
 }
 
-
-static void
-fit_write_msg_course_point(const gpsbabel::DateTime &timestamp,
-                           double position_lat, double position_long,
-                           double distance_m, const QString &name,
-                           uint8_t type) {
+void
+GarminFitFormat::fit_write_msg_course_point(const gpsbabel::DateTime& timestamp,
+    double position_lat, double position_long,
+    double distance_m, const QString& name,
+    uint8_t type) const
+{
   gbfputc(kWriteLocalIdCoursePoint, fout);
   fit_write_timestamp(timestamp);
   fit_write_position(position_lat);
@@ -1096,12 +879,12 @@ fit_write_msg_course_point(const gpsbabel::DateTime &timestamp,
   gbfputc(type, fout);
 }
 
-
-static void
-fit_write_msg_record(const gpsbabel::DateTime &timestamp,
-                     double position_lat, double position_long,
-                     double distance_m, double altitude,
-                     double speed_ms) {
+void
+GarminFitFormat::fit_write_msg_record(const gpsbabel::DateTime& timestamp,
+                                      double position_lat, double position_long,
+                                      double distance_m, double altitude,
+                                      double speed_ms) const
+{
   gbfputc(kWriteLocalIdRecord, fout);
   fit_write_timestamp(timestamp);
   fit_write_position(position_lat);
@@ -1123,9 +906,8 @@ fit_write_msg_record(const gpsbabel::DateTime &timestamp,
   }
 }
 
-
-static void
-fit_write_file_header(uint32_t file_size, uint16_t crc)
+void
+GarminFitFormat::fit_write_file_header(uint32_t file_size, uint16_t crc) const
 {
   gbfputc(kWriteHeaderCrcLen, fout); // Header+CRC length
   gbfputc(0x10, fout);               // Protocol version
@@ -1135,9 +917,8 @@ fit_write_file_header(uint32_t file_size, uint16_t crc)
   gbfputuint16(crc, fout);           // CRC
 }
 
-
-static void
-fit_write_header_msgs(const gpsbabel::DateTime& ctime, const QString& name)
+void
+GarminFitFormat::fit_write_header_msgs(const gpsbabel::DateTime& ctime, const QString& name) const
 {
   fit_write_message_def(kWriteLocalIdFileId, kIdFileId, fit_msg_fields_file_id);
   fit_write_message_def(kWriteLocalIdCourse, kIdCourse, fit_msg_fields_course);
@@ -1150,9 +931,8 @@ fit_write_header_msgs(const gpsbabel::DateTime& ctime, const QString& name)
   fit_write_msg_course(name, 0);
 }
 
-
-static void
-fit_write_file_finish()
+void
+GarminFitFormat::fit_write_file_finish() const
 {
   // Update data records size in file header
   gbsize_t file_size = gbftell(fout);
@@ -1165,7 +945,7 @@ fit_write_file_finish()
   // Update file header CRC
   uint16_t crc = 0;
   gbfseek(fout, 0, SEEK_SET);
-  for (unsigned int i = 0; i < kWriteHeaderLen; i++) {
+  for (unsigned int i = 0; i < kWriteHeaderLen; ++i) {
     int data = gbfgetc(fout);
     if (data == EOF) {
       fatal(MYNAME ": File %s truncated\n", fout->name);
@@ -1188,21 +968,21 @@ fit_write_file_finish()
   gbfputuint16(crc, fout);
 }
 
-static void
-fit_collect_track_hdr(const route_head *rte)
+void
+GarminFitFormat::fit_collect_track_hdr(const route_head* rte)
 {
   (void)rte;
   course.clear();
 }
 
-static void
-fit_collect_trackpt(const Waypoint* waypointp)
+void
+GarminFitFormat::fit_collect_trackpt(const Waypoint* waypointp)
 {
   course.push_back(FitCourseRecordPoint(*waypointp, false));
 }
 
-static void
-fit_collect_track_tlr(const route_head *rte)
+void
+GarminFitFormat::fit_collect_track_tlr(const route_head* rte)
 {
   // Prepare for writing a course corresponding to a track.
   // For this, we need to check for/synthesize missing information
@@ -1215,7 +995,7 @@ fit_collect_track_tlr(const route_head *rte)
   double prev_lat = 999, prev_lon = 999;
   double max_speed = 0;
   gpsbabel::DateTime prev_time;
-  for (auto &crpt: course) {
+  for (autocrpt: course) {
     // Distance to prev. point
     double dist;
     if (crpt.odometer_distance && crpt.odometer_distance >= dist_sum) {
@@ -1257,11 +1037,11 @@ fit_collect_track_tlr(const route_head *rte)
   // Insert course points at the right place between track points (with
   // minimum distance to next track point)
   while (!waypoints.empty()) {
-    auto &wpt = waypoints.front();
+    autowpt = waypoints.front();
     double best_distance = -1;
     auto best_distance_it = course.begin();
     double best_odometer_distance = 0;
-    for (auto cit = course.begin(); cit != course.end(); cit++) {
+    for (auto cit = course.begin(); cit != course.end(); ++cit) {
       if (!cit->is_course_point) {
         double distance = gcgeodist(cit->lat, cit->lon, wpt.lat, wpt.lon);
         if (best_distance < 0 || distance < best_distance) {
@@ -1309,7 +1089,7 @@ fit_collect_track_tlr(const route_head *rte)
   fit_write_msg_event(track_date_time, kEventTimer, kEventTypeStart, 0);
 
   // Write track/course points for the whole track
-  for (auto &crpt: course) {
+  for (autocrpt: course) {
     if (crpt.is_course_point) {
       fit_write_msg_course_point(crpt.creation_time,
                                  crpt.lat,
@@ -1330,14 +1110,14 @@ fit_collect_track_tlr(const route_head *rte)
   fit_write_msg_event(track_end_date_time, kEventTimer, kEventTypeStopDisableAll, 0);
 }
 
-static void
-fit_collect_waypt(const Waypoint* waypointp)
+void
+GarminFitFormat::fit_collect_waypt(const Waypoint* waypointp)
 {
   FitCourseRecordPoint crpt(*waypointp, true);
 
   // Try to find a better course point type than "generic", based on the
   // course point name
-  for (auto &cptm: kCoursePointTypeMapping) {
+  for (autocptm: kCoursePointTypeMapping) {
     if (crpt.shortname.contains(cptm.first, Qt::CaseInsensitive)) {
       crpt.course_point_type = cptm.second;
       break;
@@ -1347,44 +1127,29 @@ fit_collect_waypt(const Waypoint* waypointp)
   waypoints.push_back(crpt);
 }
 
-
-
 /*******************************************************************************
 * fit_write- global entry point
 *******************************************************************************/
-static void
-fit_write()
+void
+GarminFitFormat::write()
 {
   fit_write_file_header(0, 0);
   write_header_msgs = true;
-  waypt_disp_all(fit_collect_waypt);
-  track_disp_all(fit_collect_track_hdr, fit_collect_track_tlr, fit_collect_trackpt);
+
+  auto fit_collect_waypt_lambda = [this](const Waypoint* waypointp)->void {
+    fit_collect_waypt(waypointp);
+  };
+  waypt_disp_all(fit_collect_waypt_lambda);
+
+  auto fit_collect_track_hdr_lambda = [this](const route_head* rte)->void {
+    fit_collect_track_hdr(rte);
+  };
+  auto fit_collect_track_tlr_lambda = [this](const route_head* rte)->void {
+    fit_collect_track_tlr(rte);
+  };
+  auto fit_collect_trackpt_lambda = [this](const Waypoint* waypointp)->void {
+    fit_collect_trackpt(waypointp);
+  };
+  track_disp_all(fit_collect_track_hdr_lambda, fit_collect_track_tlr_lambda, fit_collect_trackpt_lambda);
   fit_write_file_finish();
 }
-
-/**************************************************************************/
-
-// capabilities below means: we can only read and write waypoints
-// please change this depending on your new module
-
-ff_vecs_t format_fit_vecs = {
-  ff_type_file,
-  {
-    ff_cap_write                               /* waypoints */,
-    (ff_cap)(ff_cap_read | ff_cap_write)       /* tracks */,
-    ff_cap_none                                /* routes */
-  },
-  fit_rd_init,
-  fit_wr_init,
-  fit_rd_deinit,
-  fit_wr_deinit,
-  fit_read,
-  fit_write,
-  nullptr,
-  &fit_args,
-  CET_CHARSET_ASCII, 0         /* ascii is the expected character set */
-  /* not fixed, can be changed through command line parameter */
-  , NULL_POS_OPS,
-  nullptr
-};
-/**************************************************************************/
diff --git a/garmin_fit.h b/garmin_fit.h
new file mode 100644 (file)
index 0000000..e5f697c
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+
+    Support for FIT track files.
+
+    Copyright (C) 2011 Paul Brook, paul@nowt.org
+    Copyright (C) 2003-2011  Robert Lipe, robertlipe+source@gpsbabel.org
+    Copyright (C) 2019 Martin Buck, mb-tmp-tvguho.pbz@gromit.dyndns.org
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+ */
+#ifndef GARMIN_FIT_H_INCLUDED_
+#define GARMIN_FIT_H_INCLUDED_
+
+#include <cstdint>              // for uint8_t, uint16_t, uint32_t
+#include <deque>                // for deque
+#include <utility>              // for pair
+#include <vector>               // for vector
+
+#include <QtCore/QList>         // for QList
+#include <QtCore/QString>       // for QString
+#include <QtCore/QVector>       // for QVector
+
+#include "defs.h"
+#include "format.h"             // for Format
+#include "gbfile.h"             // for gbfile
+#include "src/core/datetime.h"  // for DateTime
+
+
+class GarminFitFormat : public Format
+{
+public:
+  QVector<arglist_t>* get_args() override
+  {
+    return &fit_args;
+  }
+
+  ff_type get_type() const override
+  {
+    return ff_type_file;
+  }
+
+  QVector<ff_cap> get_cap() const override
+  {
+    return {
+      ff_cap_write,                            /* waypoints */
+      (ff_cap)(ff_cap_read | ff_cap_write),    /* tracks */
+      ff_cap_none                              /* routes */
+    };
+  }
+
+  QString get_encode() const override
+  {
+    return CET_CHARSET_ASCII;
+  }
+
+  int get_fixed_encode() const override
+  {
+    return 0;
+  }
+
+  void rd_init(const QString& fname) override;
+  void read() override;
+  void rd_deinit() override;
+  void wr_init(const QString& fname) override;
+  void write() override;
+  void wr_deinit() override;
+
+private:
+  /* Types */
+
+  struct fit_field_t {
+  /* MSVC 2015 generates C2664 errors without some help. */
+#if defined(_MSC_VER) && (_MSC_VER < 1910) /* MSVC 2015 or earlier */
+    fit_field_t() = default;
+    fit_field_t(int i, int s, int t) : id(i), size(s), type(t) {}
+#endif
+    int id{};
+    int size{};
+    int type{};
+  };
+
+  struct fit_message_def {
+    int endian{};
+    int global_id{};
+    QList<fit_field_t> fields;
+  };
+
+  struct fit_data_t {
+    int len{};
+    int endian{};
+    route_head* track{nullptr};
+    uint32_t last_timestamp{};
+    uint32_t global_utc_offset{};
+    fit_message_def message_def[16];
+  };
+
+  struct FitCourseRecordPoint {
+    FitCourseRecordPoint(const Waypoint& wpt, bool is_course_point, unsigned int course_point_type = kCoursePointTypeGeneric)
+      : lat(wpt.latitude),
+        lon(wpt.longitude),
+        altitude(wpt.altitude),
+        speed(WAYPT_HAS((&wpt), speed) ? wpt.speed : -1),
+        odometer_distance(wpt.odometer_distance),
+        creation_time(wpt.creation_time),
+        shortname(wpt.shortname),
+        is_course_point(is_course_point),
+        course_point_type(course_point_type) { }
+    double lat, lon, altitude;
+    double speed, odometer_distance;
+    gpsbabel::DateTime creation_time;
+    QString shortname;
+    bool is_course_point;
+    unsigned int course_point_type;
+  };
+
+  /* Constants */
+
+// constants for global IDs
+  static constexpr int kIdFileId = 0;
+  static constexpr int kIdDeviceSettings = 0;
+  static constexpr int kIdLap = 19;
+  static constexpr int kIdRecord = 20;
+  static constexpr int kIdEvent = 21;
+  static constexpr int kIdCourse = 31;
+  static constexpr int kIdCoursePoint = 32;
+
+// constants for local IDs (for writing)
+  static constexpr int kWriteLocalIdFileId = 0;
+  static constexpr int kWriteLocalIdCourse = 1;
+  static constexpr int kWriteLocalIdLap = 2;
+  static constexpr int kWriteLocalIdEvent = 3;
+  static constexpr int kWriteLocalIdCoursePoint = 4;
+  static constexpr int kWriteLocalIdRecord = 5;
+
+// constants for message fields
+// for all global IDs
+  static constexpr int kFieldTimestamp = 253;
+  static constexpr int kFieldMessageIndex = 254;
+// for global ID: file id
+  static constexpr int kFieldType = 0;
+  static constexpr int kFieldManufacturer = 1;
+  static constexpr int kFieldProduct = 2;
+  static constexpr int kFieldTimeCreated = 4;
+// for global ID: device settings
+  static constexpr int kFieldGlobalUtcOffset = 4;
+// for global ID: lap
+  static constexpr int kFieldStartTime = 2;
+  static constexpr int kFieldStartLatitude = 3;
+  static constexpr int kFieldStartLongitude = 4;
+  static constexpr int kFieldEndLatitude = 5;
+  static constexpr int kFieldEndLongitude = 6;
+  static constexpr int kFieldElapsedTime = 7;
+  static constexpr int kFieldTotalTimerTime = 8;
+  static constexpr int kFieldTotalDistance = 9;
+  static constexpr int kFieldAvgSpeed = 13;
+  static constexpr int kFieldMaxSpeed = 14;
+// for global ID: record
+  static constexpr int kFieldLatitude = 0;
+  static constexpr int kFieldLongitude = 1;
+  static constexpr int kFieldAltitude = 2;
+  static constexpr int kFieldHeartRate = 3;
+  static constexpr int kFieldCadence = 4;
+  static constexpr int kFieldDistance = 5;
+  static constexpr int kFieldSpeed = 6;
+  static constexpr int kFieldPower = 7;
+  static constexpr int kFieldTemperature = 13;
+  static constexpr int kFieldEnhancedSpeed = 73;
+  static constexpr int kFieldEnhancedAltitude = 78;
+// for global ID: event
+  static constexpr int kFieldEvent = 0;
+  static constexpr int kEnumEventTimer = 0;
+  static constexpr int kFieldEventType = 1;
+  static constexpr int kEnumEventTypeStart = 0;
+  static constexpr int kFieldEventGroup = 4;
+// for global ID: course
+  static constexpr int kFieldSport = 4;
+  static constexpr int kFieldName = 5;
+// for global ID: course point
+  static constexpr int kFieldCPTimeStamp = 1;
+  static constexpr int kFieldCPPositionLat = 2;
+  static constexpr int kFieldCPPositionLong = 3;
+  static constexpr int kFieldCPDistance = 4;
+  static constexpr int kFieldCPName = 6;
+  static constexpr int kFieldCPType = 5;
+
+// For developer fields as a non conflicting id
+  static constexpr int kFieldInvalid = 255;
+
+// types for message definitions
+  static constexpr int kTypeEnum = 0x00;
+  static constexpr int kTypeUint8 = 0x02;
+  static constexpr int kTypeString = 0x07;
+  static constexpr int kTypeUint16 = 0x84;
+  static constexpr int kTypeSint32 = 0x85;
+  static constexpr int kTypeUint32 = 0x86;
+
+// misc. constants for message fields
+  static constexpr int kFileCourse = 0x06;
+  static constexpr int kEventTimer = 0x00;
+  static constexpr int kEventTypeStart = 0x00;
+  static constexpr int kEventTypeStopDisableAll = 0x09;
+  static constexpr int kCoursePointTypeGeneric = 0x00;
+  static constexpr int kCoursePointTypeLeft = 0x06;
+  static constexpr int kCoursePointTypeRight = 0x07;
+
+  static constexpr int kWriteHeaderLen = 12;
+  static constexpr int kWriteHeaderCrcLen = 14;
+
+  static constexpr double kSynthSpeed = 10.0 * 1000 / 3600; /* speed in m/s */
+
+  /* Member Functions */
+
+  void fit_parse_header();
+  uint8_t fit_getuint8();
+  uint16_t fit_getuint16();
+  uint32_t fit_getuint32();
+  void fit_parse_definition_message(uint8_t header);
+  uint32_t fit_read_field(const fit_field_t& f);
+  void fit_parse_data(const fit_message_def& def, int time_offset);
+  void fit_parse_data_message(uint8_t header);
+  void fit_parse_compressed_message(uint8_t header);
+  void fit_parse_record();
+  void fit_write_message_def(uint8_t local_id, uint16_t global_id, const std::vector<fit_field_t>& fields) const;
+  static uint16_t fit_crc16(uint8_t data, uint16_t crc);
+  void fit_write_timestamp(const gpsbabel::DateTime& t) const;
+  void fit_write_fixed_string(const QString& s, unsigned int len) const;
+  void fit_write_position(double pos) const;
+  void fit_write_msg_file_id(uint8_t type, uint16_t manufacturer, uint16_t product, const gpsbabel::DateTime& time_created) const;
+  void fit_write_msg_course(const QString& name, uint8_t sport) const;
+  void fit_write_msg_lap(const gpsbabel::DateTime& timestamp, const gpsbabel::DateTime& start_time, double start_position_lat, double start_position_long, double end_position_lat, double end_position_long, uint32_t total_elapsed_time_s, double total_distance_m, double avg_speed_ms, double max_speed_ms) const;
+  void fit_write_msg_event(const gpsbabel::DateTime& timestamp, uint8_t event, uint8_t event_type, uint8_t event_group) const;
+  void fit_write_msg_course_point(const gpsbabel::DateTime& timestamp, double position_lat, double position_long, double distance_m, const QString& name, uint8_t type) const;
+  void fit_write_msg_record(const gpsbabel::DateTime& timestamp, double position_lat, double position_long, double distance_m, double altitude, double speed_ms) const;
+  void fit_write_file_header(uint32_t file_size, uint16_t crc) const;
+  void fit_write_header_msgs(const gpsbabel::DateTime& ctime, const QString& name) const;
+  void fit_write_file_finish() const;
+  void fit_collect_track_hdr(const route_head* rte);
+  void fit_collect_trackpt(const Waypoint* waypointp);
+  void fit_collect_track_tlr(const route_head* rte);
+  void fit_collect_waypt(const Waypoint* waypointp);
+
+  /* Data Members */
+
+  char* opt_allpoints = nullptr;
+  int lap_ct = 0;
+  bool new_trkseg = false;
+  bool write_header_msgs = false;
+
+  QVector<arglist_t> fit_args = {
+    {
+      "allpoints", &opt_allpoints,
+      "Read all points even if latitude or longitude is missing",
+      nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+    },
+  };
+
+  const std::vector<std::pair<QString, int> > kCoursePointTypeMapping = {
+    {"left", kCoursePointTypeLeft},
+    {"links", kCoursePointTypeLeft},
+    {"gauche", kCoursePointTypeLeft},
+    {"izquierda", kCoursePointTypeLeft},
+    {"sinistra", kCoursePointTypeLeft},
+
+    {"right", kCoursePointTypeRight},
+    {"rechts", kCoursePointTypeRight},
+    {"droit", kCoursePointTypeRight},
+    {"derecha", kCoursePointTypeRight},
+    {"destro", kCoursePointTypeRight},
+  };
+
+  fit_data_t fit_data;
+
+  std::deque<FitCourseRecordPoint> course, waypoints;
+
+  gbfile* fin{nullptr};
+  gbfile* fout{nullptr};
+
+  /*******************************************************************************
+  * FIT writing
+  *******************************************************************************/
+
+  const std::vector<fit_field_t> fit_msg_fields_file_id = {
+    // field id,            size, type
+    { kFieldType,           0x01, kTypeEnum   },
+    { kFieldManufacturer,   0x02, kTypeUint16 },
+    { kFieldProduct,        0x02, kTypeUint16 },
+    { kFieldTimeCreated,    0x04, kTypeUint32 },
+  };
+  const std::vector<fit_field_t> fit_msg_fields_course = {
+    { kFieldName,           0x10, kTypeString },
+    { kFieldSport,          0x01, kTypeEnum   },
+  };
+  const std::vector<fit_field_t> fit_msg_fields_lap = {
+    { kFieldTimestamp,      0x04, kTypeUint32 },
+    { kFieldStartTime,      0x04, kTypeUint32 },
+    { kFieldStartLatitude,  0x04, kTypeSint32 },
+    { kFieldStartLongitude, 0x04, kTypeSint32 },
+    { kFieldEndLatitude,    0x04, kTypeSint32 },
+    { kFieldEndLongitude,   0x04, kTypeSint32 },
+    { kFieldElapsedTime,    0x04, kTypeUint32 },
+    { kFieldTotalTimerTime, 0x04, kTypeUint32 },
+    { kFieldTotalDistance,  0x04, kTypeUint32 },
+    { kFieldAvgSpeed,       0x02, kTypeUint16 },
+    { kFieldMaxSpeed,       0x02, kTypeUint16 },
+  };
+  const std::vector<fit_field_t> fit_msg_fields_event = {
+    { kFieldTimestamp,      0x04, kTypeUint32 },
+    { kFieldEvent,          0x01, kTypeEnum   },
+    { kFieldEventType,      0x01, kTypeEnum   },
+    { kFieldEventGroup,     0x01, kTypeUint8  },
+  };
+  const std::vector<fit_field_t> fit_msg_fields_course_point = {
+    { kFieldCPTimeStamp,    0x04, kTypeUint32 },
+    { kFieldCPPositionLat,  0x04, kTypeSint32 },
+    { kFieldCPPositionLong, 0x04, kTypeSint32 },
+    { kFieldCPDistance,     0x04, kTypeUint32 },
+    { kFieldCPName,         0x10, kTypeString },
+    { kFieldCPType,         0x01, kTypeEnum   },
+  };
+  const std::vector<fit_field_t> fit_msg_fields_record = {
+    { kFieldTimestamp,      0x04, kTypeUint32 },
+    { kFieldLatitude,       0x04, kTypeSint32 },
+    { kFieldLongitude,      0x04, kTypeSint32 },
+    { kFieldDistance,       0x04, kTypeUint32 },
+    { kFieldAltitude,       0x02, kTypeUint16 },
+    { kFieldSpeed,          0x02, kTypeUint16 },
+  };
+};
+#endif // GARMIN_FIT_H_INCLUDED_
diff --git a/vecs.h b/vecs.h
index 7a0d57fa3a35393a80a6ae8d5c35960010208d96..e482c4c2f0de28dc82a56c11561b9d88353e4a4b 100644 (file)
--- a/vecs.h
+++ b/vecs.h
@@ -30,6 +30,7 @@
 #include "defs.h"
 #include "format.h"
 #include "energympro.h"
+#include "garmin_fit.h"
 #include "geojson.h"
 #include "ggv_bin.h"
 #include "globalsat_sport.h"
@@ -172,7 +173,6 @@ extern ff_vecs_t miniHomer_vecs;
 extern ff_vecs_t jogmap_vecs;
 extern ff_vecs_t wintec_tes_vecs;
 extern ff_vecs_t format_garmin_xt_vecs;
-extern ff_vecs_t format_fit_vecs;
 extern ff_vecs_t mapbar_track_vecs;
 extern ff_vecs_t f90g_track_vecs;
 extern ff_vecs_t mapfactor_vecs;
@@ -402,7 +402,7 @@ private:
   LegacyFormat wintec_tes_fmt {wintec_tes_vecs};
   SubripFormat subrip_fmt;
   LegacyFormat format_garmin_xt_fmt {format_garmin_xt_vecs};
-  LegacyFormat format_fit_fmt {format_fit_vecs};
+  GarminFitFormat format_fit_fmt;
   LegacyFormat mapbar_track_fmt {mapbar_track_vecs};
   LegacyFormat f90g_track_fmt {f90g_track_vecs};
   LegacyFormat mapfactor_fmt {mapfactor_vecs};